from alipay import AliPay
from django.core.paginator import Paginator, EmptyPage
from django.core.urlresolvers import reverse
from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.views.generic import View

from django.core.cache import cache

from goods.models import GoodsSKU
from orders.models import OrderInfo, OrderGoods
from users.models import Address
from django_redis import get_redis_connection
from django.utils import timezone
from django.conf import settings
from utils.views import LoginRequiredMixin, LoginRequiredJsonMixin, TransactionAtomicMixin
# Create your views here.
# import json


class CommentView(LoginRequiredMixin, View):
    """订单评论"""
    def get(self, request, order_id):
        """提供评论页面"""
        user = request.user
        try:
            order = OrderInfo.objects.get(order_id=order_id, user=user)
        except OrderInfo.DoesNotExist:
            return redirect(reverse("orders:info"))

        order.status_name = OrderInfo.ORDER_STATUS[order.status]
        order.skus = []
        order_skus = order.ordergoods_set.all()
        for order_sku in order_skus:
            sku = order_sku.sku
            sku.count = order_sku.count
            sku.amount = sku.price * sku.count
            order.skus.append(sku)
        return render(request, "order_comment.html", {'order':order})

    def post(self, request, order_id):
        """处理评论内容"""
        user = request.user
        try:
            order = OrderInfo.objects.get(order_id=order_id, user=user)
        except OrderInfo.DoesNotExist:
            return redirect(reverse('orders:info'))

        # 获取评论条数
        total_count = request.POST.get('total_count')
        total_count = int(total_count)
        # 从第一条开始，不会有第0条，所以从1开始遍历
        for i in range(1, total_count + 1):
            sku_id = request.POST.get('sku_%d' % i)
            # 没有的话为空
            content = request.POST.get('content_%d' % i, '')
            try:
                order_goods = OrderGoods.objects.get(order=order, sku_id=sku_id)
            except OrderGoods.DoesNotExist:
                continue
            order_goods.comment = content
            order_goods.save()
            # 清楚商品详情的缓存
            cache.delete('detail_%s' % sku_id)

        order.status = OrderInfo.ORDER_STATUS_ENUM['FINISHED']
        order.save()

        return redirect(reverse('orders:info', kwargs={'page':1}))





class CheckPayView(LoginRequiredJsonMixin, View):
    """检查订单订单"""
    def get(self, request):
        # 获取id
        order_id = request.GET.get('order_id')
        # 校验id
        if not order_id:
            return JsonResponse({'code':2, 'message':'订单id错误'})
        # 获取订单信息
        try:
            order = OrderInfo.objects.get(
                order_id=order_id,
                user=request.user,
                status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'],
                pay_method=OrderInfo.PAY_METHODS_ENUM['ALIPAY']
            )
        except OrderInfo.DoesNotExist:
            return JsonResponse({'code':3, 'message':'订单123456错误'})
        # 创建支付宝支付的对象
        alipay = AliPay(
            appid=settings.ALIPAY_APPID,
            app_notify_url=None,
            app_private_key_path=settings.APP_PRIVATE_KEY_PATH,
            alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH,
            sign_type='RSA2',
            debug=True,
        )
        # 查询支付结果
        while True:
            # 循环等待网络延迟造成的问题
            response = alipay.api_alipay_trade_query(order_id)
            code = response.get('code')
            trade_status = response.get('trade_status')

        # 判断支付结果
            if code == '10000' and trade_status == 'TRADE_SUCCESS':
            # 支付成功
                # 设置订单为待评论
                order.status = OrderInfo.ORDER_STATUS_ENUM['UNCOMMENT']
                # 设置支付宝的订单编号
                order.trade_id = response.get('trade_no')
                order.save()
            # 判断其他支付状态，订单支付可能有延迟
            # 判断用户支付失败，返回失败结果
                return JsonResponse({'code':0, 'message':'支付成功'})
            elif code == '40004' or (code == '10000' and trade_status == 'WAIT_BUYER_PAY'):
                # 表示支付宝的接口暂时调用失败，网络延迟，订单还未生成；or 等待订单的支付
                # 继续查询
                continue
            else:
                # 支付失败，返回支付失败的通知
                return JsonResponse({'code': 4, 'message': '支付失败'})




class PayView(LoginRequiredJsonMixin, View):
    """订单支付"""

    def post(self, request):
        # 获取订单id
        order_id = request.POST.get('order_id')
        # 校验订单id
        if not order_id:
            return JsonResponse({'code':2, 'message':'订单id错误'})

        # 获取订单信息，:状态为待支付，方式为支付宝
        try:
            order = OrderInfo.objects.get(order_id=order_id, user=request.user,
                                          status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'],
                                          pay_method=OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                                          )
        except OrderInfo.DoesNotExist:
            return JsonResponse({'code':3, 'message':'订单粗存在错误'})


        # 创建用于支付宝支付的对象
        alipay = AliPay(
            appid=settings.ALIPAY_APPID,
            app_notify_url=None,  # 默认回调url
            # 自己生产的私钥
            app_private_key_path=settings.APP_PRIVATE_KEY_PATH,
            # 支付宝生成的公钥
            alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH,
            sign_type='RSA2',
            debug=True    # 测试模式，沙箱
        )
        # 电脑网站支付，需要跳转到https://openapi.alipay.com/gateway.do? + order_string
        order_string = alipay.api_alipay_trade_page_pay(
            out_trade_no=order_id,
            total_amount=str(order.total_amount),  # 转为为字符串
            subject='天天生鲜',
            return_url=None,
            notify_url=None  # 可选，这样为默认url
        )
        print(order_string)
        # 生成url，让用户进入支付宝页面的支付网址
        url = settings.ALIPAY_URL + '?' + order_string

        # 响应结果
        return JsonResponse({'code': 0, 'message':'支付成功', 'url':url})


class UserOrderView(LoginRequiredMixin, View):
    """用户订单页面"""
    def get(self, request, page):
        """提供订单信息页面"""
        user = request.user
        # 查询所有订单
        orders = user.orderinfo_set.all().order_by('-create_time')
        # 遍历订单
        for order in orders:
            # 给订单绑定状态
            order.status_name = OrderInfo.ORDER_STATUS[order.status]
            # 绑定支付方式
            order.pay_method_name = OrderInfo.PAY_METHODS[order.pay_method]
            order.skus = []
            # 查询订单中的商品
            order_skus = order.ordergoods_set.all()
            for order_sku in order_skus:
                sku = order_sku.sku
                sku.count = order_sku.count
                sku.amount = sku.price * sku.count
                order.skus.append(sku)
        page = int(page)   # 分页
        paginator = Paginator(orders, 2)

        try:
            page_orders = paginator.page(page)
        except EmptyPage:
            # 如果没有查询页数，就返回第一页
            page_orders = paginator.page(1)
        # 页数
        page_list = paginator.page_range

        context = {
            'orders': page_orders,
            'page': page,
            'page_list': page_list,
        }
        return render(request, "user_center_order.html", context)


class CommitOrderView(LoginRequiredJsonMixin,TransactionAtomicMixin, View):
    """
    提交订单
    提交订单通过ajax的方式，所以这里都是通过ajax和json的方式对接，而不是表单提交
    这时，login_requried的登陆验证不能满足要求，需要重新定义登陆验证装饰器

    """
    def post(self, request):

        # 获取参数：user,address_id,pay_method,sku_ids,count
        user = request.user
        address_id = request.POST.get('address_id')
        pay_method = request.POST.get('pay_method')
        # sku_ids的获取是通过
        # print(json.loads(pay_method))
        sku_ids = request.POST.get('sku_ids')
        # print(2)
        # print(json.loads(sku_ids))

        # 校验参数：all([address_id, pay_method, sku_ids])
        if not all([address_id, sku_ids, pay_method]):
            return JsonResponse({'code':2, 'message': '缺少参数'})
        # 判断地址
        try:
            address = Address.objects.get(id=address_id)
        except Address.DoesNotExist:
            return JsonResponse({'code': 3, 'message':'地址不存在'})
        # 判断支付方式
        if pay_method not in OrderInfo.PAY_METHOD:
            return JsonResponse({'code': 4, 'message':'支付方式错误'})
        # 创建redis链接，取出字典
        redis_conn = get_redis_connection('default')
        cart_dict = redis_conn.hgetall('cart_%s' % user.id)
        # 截取出sku_ids列表
        sku_ids = sku_ids.split(',')
        total_count = 0
        total_amount = 0
        # 手动生成order_id
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + str(user.id)

        # 创建事务的保存点
        save_point = transaction.savepoint()

        # 暴力回滚，即把事务完成之前的所有部分放进try，有问题则回滚
        # 创建订单，订单和订单商品是一对多的关系
        try:
            order = OrderInfo.objects.create(
                order_id=order_id,
                user=user,
                address=address,
                total_amount=total_amount,
                trans_cost=10,
                pay_method=pay_method
            )
            # 遍历sku_ids
            # 循环取出sku，判断商品是否存在
            for sku_id in sku_ids:
                for i in range(3):
                    # 循环三次，用户单次处理时如果数据被锁，或者更新不及时，下一次可以
                    # 正确操作，如果三次都不能，说明有问题，下单失败
                    try:
                        sku = GoodsSKU.objects.get(id=sku_id)
                    except Exception:
                        return JsonResponse({'code': 5, 'message': '商品不存在'})

                # 获取商品数量，判断库存 (redis)
                    sku_count = cart_dict.get(sku_id.encode())
                    sku_count = int(sku_count)
                    if sku_count > sku.stock:
                        return JsonResponse({'code': 6, 'message':'库存不足'})

                    # 减少sku库存
                    # sku.stock -= sku_count
                    # # 增加sku销量
                    # sku.sales += sku_count
                    # sku.save()

                    # #　※　添加乐观锁减少库存和增加销量
                    origin_stock = sku.stock
                    new_stock = origin_stock - sku_count
                    new_sales = sku.sales + sku_count

                    result = GoodsSKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock,sales=new_sales)
                    if 0 == result and i < 2:
                        continue
                        # 查询没有结果，一般为库存有变化，ｉ<2时，就直接进入下一次循环
                    elif 0 == result and i ==2:
                        # 库存在你更新时,已经被人提前更新了,就是你要买的商品,别人在你前面买走了一些,你需要注意库存
                        return JsonResponse({'code': 8, 'message': '下单失败,检查库存'})

                    # 保存订单商品数据OrderGoods(能执行到这里说明无异常)
                    OrderGoods.objects.create(
                        order=order,     # 外键
                        sku=sku,
                        count=sku_count,
                        price=sku.price
                    )
                    total_count += sku_count
                    total_amount += (sku_count*sku.price)
                    break

                # 修改订单信息里面的总数和总金额(OrderInfo)
                order.total_count = total_count
                order.total_amount = total_amount+10
                # 订单生成后删除购物车(hdel)
                order.save()
        except Exception:
            transaction.savepoint_rollback(save_point)
            return JsonResponse({'code':7, 'message':'下单失败'})
        transaction.savepoint_commit(save_point)
        # 加※的用法是将cart_user_id中以sku_ids列表中元素为field的字段删除
        # 比循环遍历要简单
        redis_conn.hdel('cart_%s'%user.id, *sku_ids)
        return JsonResponse({'code':0, 'message':'下单成功'})


class PlaceOrderView(LoginRequiredMixin, View):
    """处理订单确认页面
        这一步必须为登陆用户
        注意数据库处理的事务和锁（乐观锁）
    """
    def post(self, request):
        # 购物车中的sku_ids为一键多值，采用
        sku_ids = request.POST.getlist('sku_ids')
        # 用户从详情点击立即购买过来时，才会有count，从购物车过来的话，count从redis中查询
        count = request.POST.get('count')

        # 定义临时容器
        skus = []             # 存放商品的容器
        total_count = 0       # 总数
        total_sku_amount = 0  # 商品款
        trans_cost = 10       # 运费
        total_amount = 0  # 实付款

        if not sku_ids:
            # 如果没有sku——ids，从定向到购物车，重新选择
            return redirect(reverse('cart:info'))
        redis_conn = get_redis_connection('default')
        user_id = request.user.id

        # 查询商品数据
        if count is None:
            # 从购物车页面访问过来，count可以从redis中读取
            cart_dict = redis_conn.hgetall('cart_%s' % user_id)

            # 遍历商品的sku_ids
            for sku_id in sku_ids:
                try:
                    sku = GoodsSKU.objects.get(id=sku_id)
                except GoodsSKU.DoesNotExist:
                    # 查询不到则回复到购物车
                    # 可能用户在购物车界面延迟太久，而数据库中内容有变化，就可能会有不存在的问题
                    # 或者跨越浏览器的违法访问
                    return redirect(reverse('cart:info'))
                sku_count = cart_dict.get(sku_id.encode())
                sku_count = int(sku_count)

                # 计算商品金额
                amount = sku.price * sku_count
                sku.count = sku_count
                sku.amount = amount
                skus.append(sku)
                # 金额和数量求和
                total_count += sku_count
                total_sku_amount += amount

        else:
            # 从商品详情页立即购买过来，商品的数量从request中获取
            # 同样遍历sku_ids只是这种情况下，只有一个值
            for sku_id in sku_ids:
                # 确认商品存在
                try:
                    sku = GoodsSKU.objects.get(id=sku_id)
                except GoodsSKU.DoesNotExist:
                    # 商品不存在，
                    return redirect(reverse('cart:info'))
                # 获取count，验证
                try:
                    sku_count = int(count)
                except Exception:
                    return redirect(reverse('goods:detail', args=sku_id))

                # 没有判断库存
                if sku_count > sku.stock:
                    return redirect(reverse('goods:detail', args=sku_id))

                # 计算商品的总金额
                amount = sku.price * sku_count
                sku.count = sku_count
                sku.amount = amount
                skus.append(sku)
                # 金额和数量求和
                total_count += sku_count
                total_sku_amount += amount
                redis_conn.hset('cart_%s' % user_id, sku_id, sku_count)

        # 实付款
        total_amount = total_sku_amount + trans_cost

        # 查询用户地址信息
        try:
            address = Address.objects.filter(user=request.user).latest('create_time')
        except Address.DoesNotExist:
            # 在模板中会判断跳转到地址编辑页面
            address = None

        # 构造上下文
        context = {
            'skus': skus,
            'total_count': total_count,
            'total_sku_amount': total_sku_amount,
            'trans_cost': trans_cost,
            'total_amount': total_amount,
            'address': address,
            'sku_ids': ','.join(sku_ids),
        }
        return render(request, 'place_order.html', context)


